import { POINTS, POINTS_NOTIFICATIONS_TOGGLE, useExperimentalFlag } from '@/config';
import { Box } from '@/design-system';
import { IS_TEST } from '@/env';
import { isTestnetChain } from '@/handlers/web3';
import { removeFirstEmojiFromString, returnStringFirstEmoji } from '@/helpers/emojiHandler';
import WalletTypes from '@/helpers/walletTypes';
import { useAccountSettings, useAppState } from '@/hooks';
import { useRemoteConfig } from '@/model/remoteConfig';
import { RainbowAccount } from '@/model/wallet';
import { useNavigation } from '@/navigation';
import Routes from '@/navigation/routesNames';
import { isNotificationPermissionGranted, requestNotificationPermission } from '@/notifications/permissions';
import {
  useAllNotificationSettingsFromStorage,
  useWalletGroupNotificationSettings,
  WalletNotificationRelationship,
  WalletNotificationSettings,
} from '@/notifications/settings';
import { GlobalNotificationTopic } from '@/notifications/settings/constants';
import { toggleGlobalNotificationTopic } from '@/notifications/settings/settings';
import { getNotificationSettingsForWalletWithAddress, setAllGlobalNotificationSettingsToStorage } from '@/notifications/settings/storage';
import { GlobalNotificationTopics, GlobalNotificationTopicType } from '@/notifications/settings/types';
import { SettingsLoadingIndicator } from '@/screens/SettingsSheet/components/SettingsLoadingIndicator';
import { showNotificationSubscriptionErrorAlert, showOfflineAlert } from '@/screens/SettingsSheet/components/notificationAlerts';
import { abbreviations, deviceUtils } from '@/utils';
import profileUtils from '@/utils/profileUtils';
import { useNetInfo } from '@react-native-community/netinfo';
import { useFocusEffect } from '@react-navigation/native';
import * as i18n from '@/languages';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { Alert, Linking, Switch } from 'react-native';
import { checkNotifications, RESULTS } from 'react-native-permissions';
import { ContactAvatar } from '../../../components/contacts';
import ImageAvatar from '../../../components/contacts/ImageAvatar';
import { useWallets, useWalletsStore } from '@/state/wallets/walletsStore';
import Menu from './Menu';
import MenuContainer from './MenuContainer';
import MenuItem from './MenuItem';

type WalletRowProps = {
  ens: string;
  groupOff: boolean;
  isTestnet: boolean;
  loading?: boolean;
  notificationSettings: WalletNotificationSettings[];
  wallet: RainbowAccount;
};

type WalletRowLabelProps = {
  groupOff: boolean;
  notifications?: WalletNotificationSettings;
};

const DEVICE_WIDTH = deviceUtils.dimensions.width;
const AMOUNT_OF_TOPICS_TO_DISPLAY = DEVICE_WIDTH > 400 ? 3 : 2;

const WalletRowLabel = ({ notifications, groupOff }: WalletRowLabelProps) => {
  const composedLabel = useMemo(() => {
    if (!notifications) return i18n.t(i18n.l.settings.notifications_section.off);
    const allTopicsEnabled = Object.values(notifications.topics).every(topic => topic);
    const allTopicsDisabled = groupOff || Object.values(notifications.topics).every(topic => !topic);
    const enabledTopics = Object.keys(notifications.topics).filter(topic => notifications.topics[topic as unknown as number]);

    if (allTopicsDisabled) {
      return i18n.t(i18n.l.settings.notifications_section.off);
    }

    if (notifications.enabled) {
      if (allTopicsEnabled) {
        return i18n.t(i18n.l.settings.notifications_section.all);
      }

      if (enabledTopics.length > AMOUNT_OF_TOPICS_TO_DISPLAY) {
        const limitedTopics = enabledTopics
          .slice(0, AMOUNT_OF_TOPICS_TO_DISPLAY)
          .map(topic => i18n.t((i18n.l.settings.notifications_section as any)[topic]))
          .join(', ');

        return `${limitedTopics} ${i18n.t(i18n.l.settings.notifications_section.plus_n_more, {
          n: enabledTopics.length - AMOUNT_OF_TOPICS_TO_DISPLAY,
        })}`;
      } else {
        return enabledTopics.map(topic => i18n.t((i18n.l.settings.notifications_section as any)[topic])).join(', ');
      }
    } else {
      return i18n.t(i18n.l.settings.notifications_section.off);
    }
  }, [groupOff, notifications]);

  return <MenuItem.Label text={composedLabel} />;
};

const WalletRow = ({ ens, groupOff, isTestnet, loading, notificationSettings, wallet }: WalletRowProps) => {
  const { navigate } = useNavigation();
  const notificationSetting = notificationSettings?.find((x: WalletNotificationSettings) => x.address === wallet.address);
  const cleanedUpLabel = useMemo(() => removeFirstEmojiFromString(wallet.label), [wallet.label]);

  const displayAddress = useMemo(() => abbreviations.address(wallet.address, 4, 6), [wallet.address]);
  const walletName = cleanedUpLabel || ens || displayAddress || '';

  const navigateToWalletSettings = useCallback(
    (name: string, address: string) => {
      const settingsForWallet = getNotificationSettingsForWalletWithAddress(address);

      if (settingsForWallet) {
        navigate(Routes.WALLET_NOTIFICATIONS_SETTINGS, {
          title: name,
          notificationSettings: settingsForWallet,
          address,
        });
      } else {
        Alert.alert(
          i18n.t(i18n.l.settings.notifications_section.no_settings_for_address_title),
          i18n.t(i18n.l.settings.notifications_section.no_settings_for_address_content),
          [{ text: 'OK' }]
        );
      }
    },
    [navigate]
  );

  const rowEnabled = useMemo(() => {
    const enabledTopics = notificationSetting ? Object.values(notificationSetting.topics).filter(topic => Boolean(topic)) : [];
    return !groupOff && enabledTopics.length && notificationSetting?.enabled;
  }, [groupOff, notificationSetting]);

  return (
    <MenuItem
      disabled={isTestnet || loading}
      key={wallet.address}
      hasRightArrow={!isTestnet}
      labelComponent={<WalletRowLabel notifications={notificationSetting} groupOff={groupOff} />}
      leftComponent={
        <Box
          style={{
            opacity: rowEnabled ? 1 : 0.25,
          }}
        >
          {wallet.image ? (
            <ImageAvatar image={wallet.image} size={rowEnabled ? 'smedium' : 'smedium_shadowless'} />
          ) : (
            <ContactAvatar
              color={wallet.color}
              size={rowEnabled ? 'small' : 'small_shadowless'}
              value={returnStringFirstEmoji(wallet.label) || profileUtils.addressHashedEmoji(wallet.address)}
            />
          )}
        </Box>
      }
      onPress={() => navigateToWalletSettings(walletName, wallet.address)}
      size={52}
      titleComponent={<MenuItem.Title text={walletName} />}
    />
  );
};

const NotificationsSection = () => {
  const { justBecameActive } = useAppState();
  const { navigate } = useNavigation();
  const { chainId } = useAccountSettings();
  const isTestnet = isTestnetChain({ chainId });
  const wallets = useWallets();
  const walletNames = useWalletsStore(state => state.walletNames);
  const { isConnected } = useNetInfo();
  const { points_enabled, points_notifications_toggle } = useRemoteConfig();
  const pointsEnabled = useExperimentalFlag(POINTS) || points_enabled || IS_TEST;
  const pointsNotificationsToggleEnabled = useExperimentalFlag(POINTS_NOTIFICATIONS_TOGGLE) || points_notifications_toggle;

  const {
    ownerEnabled: storedOwnerEnabled,
    updateGroupSettingsAndSubscriptions,
    watcherEnabled: storedWatcherEnabled,
  } = useWalletGroupNotificationSettings();
  const { globalNotificationSettings, walletNotificationSettings } = useAllNotificationSettingsFromStorage();

  const [topicState, setTopicState] = useState<GlobalNotificationTopics>(globalNotificationSettings);
  const toggleStateForTopic = (topic: GlobalNotificationTopicType) => setTopicState(prev => ({ ...prev, [topic]: !prev[topic] }));

  // local state controls the switch UI for better UX
  const [ownedState, setOwnedState] = useState({
    status: storedOwnerEnabled,
    loading: false,
  });
  const [watchedState, setWatchedState] = useState({
    status: storedWatcherEnabled,
    loading: false,
  });
  // We allow only one subscription in progress
  // this states controls which we are currently updating
  const [topicSubscriptionInProgress, setTopicSubscriptionInProgress] = useState<GlobalNotificationTopicType | null>(null);

  const { ownedWallets, watchedWallets } = useMemo(() => {
    const ownedWallets: RainbowAccount[] = [];
    const watchedWallets: RainbowAccount[] = [];
    // group wallets by relationship if the arrays are empty
    if (watchedWallets.length === 0 && ownedWallets.length === 0) {
      const walletIDs = Object.keys(wallets ?? {});
      walletIDs.forEach(key => {
        const wallet = wallets?.[key];

        if (wallet?.type === WalletTypes.readOnly) {
          wallet?.addresses.forEach(item => item.visible && watchedWallets.push({ ...item }));
        } else {
          wallet?.addresses.forEach(item => item.visible && ownedWallets.push(item));
        }
      });
    }

    return { ownedWallets, watchedWallets };
  }, [wallets]);

  useFocusEffect(
    useCallback(() => {
      setOwnedState({ loading: false, status: storedOwnerEnabled });
      setWatchedState({ loading: false, status: storedWatcherEnabled });
    }, [storedOwnerEnabled, storedWatcherEnabled])
  );

  const noOwnedWallets = !ownedWallets.length;
  const noWatchedWallets = !watchedWallets.length;
  const [permissionStatus, setPermissionStatus] = useState<string>('');
  const neverGranted = permissionStatus === RESULTS.DENIED;
  const disabledInSystem = permissionStatus === RESULTS.BLOCKED;

  const toggleAllOwnedNotifications = useCallback(async () => {
    if (!isConnected) {
      showOfflineAlert();
      return;
    }
    setOwnedState(prev => ({ status: !prev.status, loading: true }));
    const isSuccess = await updateGroupSettingsAndSubscriptions(WalletNotificationRelationship.OWNER, !storedOwnerEnabled);
    if (isSuccess) {
      setOwnedState(prev => ({ ...prev, loading: false }));
    } else {
      showNotificationSubscriptionErrorAlert();
      setOwnedState(prev => ({ status: !prev.status, loading: false }));
    }
  }, [storedOwnerEnabled, updateGroupSettingsAndSubscriptions, isConnected]);

  const toggleAllWatchedNotifications = useCallback(async () => {
    if (!isConnected) {
      showOfflineAlert();
      return;
    }
    setWatchedState(prev => ({ status: !prev.status, loading: true }));
    const isSuccess = await updateGroupSettingsAndSubscriptions(WalletNotificationRelationship.WATCHER, !storedWatcherEnabled);
    if (isSuccess) {
      setWatchedState(prev => ({ ...prev, loading: false }));
    } else {
      showNotificationSubscriptionErrorAlert();
      setWatchedState(prev => ({ status: !prev.status, loading: false }));
    }
  }, [updateGroupSettingsAndSubscriptions, storedWatcherEnabled, isConnected]);

  const toggleTopic = useCallback(
    (topic: GlobalNotificationTopicType) => {
      if (!isConnected) {
        showOfflineAlert();
        return;
      }
      toggleStateForTopic(topic);
      setTopicSubscriptionInProgress(topic);
      toggleGlobalNotificationTopic(topic, !globalNotificationSettings[topic])
        .then(() => {
          setAllGlobalNotificationSettingsToStorage({
            ...topicState,
            [topic]: !topicState[topic],
          });
        })
        .catch(() => {
          showNotificationSubscriptionErrorAlert();
          toggleStateForTopic(topic);
        })
        .finally(() => {
          setTopicSubscriptionInProgress(null);
        });
    },
    [globalNotificationSettings, isConnected, topicState]
  );

  const openSystemSettings = Linking.openSettings;
  const openNetworkSettings = useCallback(() => navigate(Routes.NETWORK_SWITCHER), [navigate]);

  const requestNotificationPermissions = useCallback(async () => {
    const status = await requestNotificationPermission();
    const allowed = isNotificationPermissionGranted(status);
    if (allowed) {
      setPermissionStatus(RESULTS.GRANTED);
    } else {
      openSystemSettings();
    }
  }, [openSystemSettings]);

  const checkPermissions = useCallback(async () => {
    checkNotifications().then(({ status }) => {
      setPermissionStatus(status);
    });
  }, []);

  useEffect(() => {
    checkPermissions();
  }, [checkPermissions, justBecameActive]);

  return (
    <Box>
      <MenuContainer
        // required key to force re-render when permission status changes
        // while the app is in the background
        key={permissionStatus}
      >
        {neverGranted && (
          <Menu description={i18n.t(i18n.l.settings.notifications_section.no_first_time_permissions)}>
            <MenuItem
              hasSfSymbol
              size={52}
              leftComponent={<MenuItem.TextIcon icon="􀝖" isLink />}
              titleComponent={
                <MenuItem.Title text={i18n.t(i18n.l.settings.notifications_section.first_time_allow_notifications)} weight="bold" isLink />
              }
              onPress={requestNotificationPermissions}
            />
          </Menu>
        )}

        {disabledInSystem && (
          <Menu description={i18n.t(i18n.l.settings.notifications_section.no_permissions)}>
            <MenuItem
              hasSfSymbol
              size={52}
              leftComponent={<MenuItem.TextIcon icon="􀍟" isLink />}
              titleComponent={
                <MenuItem.Title text={i18n.t(i18n.l.settings.notifications_section.open_system_settings)} weight="bold" isLink />
              }
              onPress={openSystemSettings}
            />
          </Menu>
        )}

        {isTestnet ? (
          <Menu description={i18n.t(i18n.l.settings.notifications_section.unsupported_network)}>
            <MenuItem
              hasSfSymbol
              size={52}
              leftComponent={<MenuItem.TextIcon icon="􀇂" isLink />}
              titleComponent={<MenuItem.Title text={i18n.t(i18n.l.settings.notifications_section.change_network)} weight="bold" isLink />}
              onPress={openNetworkSettings}
            />
          </Menu>
        ) : (
          <>
            <Menu description={noOwnedWallets ? i18n.t(i18n.l.settings.notifications_section.no_owned_wallets) : ''}>
              <MenuItem
                disabled
                rightComponent={
                  <>
                    {ownedState.loading && <SettingsLoadingIndicator />}
                    <Switch
                      disabled={noOwnedWallets || isTestnet || ownedState.loading}
                      onValueChange={toggleAllOwnedNotifications}
                      value={ownedState.status}
                    />
                  </>
                }
                size={52}
                titleComponent={<MenuItem.Title text={i18n.t(i18n.l.settings.notifications_section.my_wallets)} weight="bold" />}
              />
              {ownedWallets.map(wallet => (
                <WalletRow
                  ens={walletNames[wallet.address]}
                  groupOff={!storedOwnerEnabled}
                  isTestnet={isTestnet}
                  key={wallet.address}
                  loading={ownedState.loading}
                  notificationSettings={walletNotificationSettings}
                  wallet={wallet}
                />
              ))}
            </Menu>
            <Menu description={noWatchedWallets ? i18n.t(i18n.l.settings.notifications_section.no_watched_wallets) : ''}>
              <MenuItem
                disabled
                rightComponent={
                  <>
                    {watchedState.loading && <SettingsLoadingIndicator />}
                    <Switch
                      disabled={noWatchedWallets || isTestnet || watchedState.loading}
                      onValueChange={toggleAllWatchedNotifications}
                      value={watchedState.status}
                    />
                  </>
                }
                size={52}
                titleComponent={<MenuItem.Title text={i18n.t(i18n.l.settings.notifications_section.watched_wallets)} weight="bold" />}
              />
              {watchedWallets.map(wallet => (
                <WalletRow
                  ens={walletNames[wallet.address]}
                  groupOff={!storedWatcherEnabled}
                  isTestnet={isTestnet}
                  key={wallet.address}
                  loading={watchedState.loading}
                  notificationSettings={walletNotificationSettings}
                  wallet={wallet}
                />
              ))}
            </Menu>
          </>
        )}
        <Menu>
          {pointsEnabled && pointsNotificationsToggleEnabled && (
            <MenuItem
              disabled
              rightComponent={
                <>
                  {topicSubscriptionInProgress === GlobalNotificationTopic.POINTS && <SettingsLoadingIndicator />}
                  <Switch
                    disabled={topicSubscriptionInProgress !== null}
                    onValueChange={() => toggleTopic(GlobalNotificationTopic.POINTS)}
                    value={topicState[GlobalNotificationTopic.POINTS]}
                  />
                </>
              }
              size={52}
              titleComponent={<MenuItem.Title text={i18n.t(i18n.l.settings.notifications_section.points)} weight="bold" />}
            />
          )}
        </Menu>
      </MenuContainer>
    </Box>
  );
};

export default NotificationsSection;
